/// libgrf.h : main header file
///
/// Please include this file in your C/C++ programs to be able to use the GRF functions.
/// Remember to always call grf_free() after using a GRF file to avoid memory leaks.
#pragma once
#include "stdint.h"
#define GRFEXPORT extern

class GrfFile;
class GrfNode;
class GrfTreeNode;
typedef GrfFile* grf_handle;
typedef GrfNode* grf_node;
typedef GrfTreeNode* grf_treenode;

#ifdef __cplusplus
extern "C" {
#endif

/// version information
#define VERSION_MAJOR 0
#define VERSION_MINOR 1
#define VERSION_REVISION 31
#define VERSION_AUTHOR "MagicalTux <MagicalTux@ooKoo.org>"

/// grf entry flags (bitfield)
/// @see grf_file_get_storage_flags()
#define GRF_FLAG_FILE     1
#define GRF_FLAG_MIXCRYPT 2
#define GRF_FLAG_DES      4

/// grf repack options
/// @see grf_merge() and grf_repack()
#define GRF_REPACK_FAST       1 // only move data, do not care about what we move
#define GRF_REPACK_DECRYPT    2 // if we're about to move encrypted data, decrypt it on the fly
#define GRF_REPACK_RECOMPRESS 3 // repack, extract each file, recompress them, and write them

/// callback function type
typedef bool (grf_callback)(void* param, grf_handle handle, int position, int max, const char* filename);

/// *****************************************************************************
/// *************************** BASE FUNCTIONS **********************************
/// *****************************************************************************

/// Returns the current GRF version as an integer.
/// You can get major/minor/revision values with the following math:
/// major    = (version >> 16) & 0xff
/// minor    = (version >>  8) & 0xff
/// revision = (version >>  0) & 0xff
GRFEXPORT uint32_t grf_version(void);

/// Returns the version of libgrf (and zlib) as a string.
/// You can use the reentrant version of this function for thread-safe usage.
GRFEXPORT char* grf_versionstring(void);
GRFEXPORT char* grf_versionstring_r(char* buffer, size_t len);

/// Creates a new GRF handle to the specified file.
/// Flag 'allow_write' controls write access.
/// The first form opens the file internally, the second leaves it up to the caller.
GRFEXPORT grf_handle grf_new(const char* filename, bool allow_write);
GRFEXPORT grf_handle grf_new_by_fd(int fd, bool allow_write);

/// Loads GRF data and returns a GRF handle.
/// The first form invokes grf_new() before using the second form.
GRFEXPORT grf_handle grf_load(const char* filename, bool allow_write);
GRFEXPORT grf_handle grf_load_from_new(grf_handle handle);

/// Writes the grf's files table to disk.
GRFEXPORT bool grf_save(grf_handle handle);

/// Frees a GRF file and all the memory used by it, and closes the grf's fd.
/// If any change was made to the grf, a best-effort grf_save() is also called.
GRFEXPORT void grf_free(grf_handle handle);

/// Defines a callback function for all the long operations. This includes loading, repack, merge, etc...
GRFEXPORT void grf_set_callback(grf_handle handle, grf_callback* callback, void* callback_param);

/// Sets the compression level used on this GRF file.
/// This affects newly added files, repack/merge when using GRF_REPACK_RECOMPRESS mode, and files table.
/// The level is directly passed to zlib, and should be between 0 (no compression) and 9.
GRFEXPORT void grf_set_compression_level(grf_handle handle, int level);

/// Returns the number of files currently in the GRF. Directory entries are excluded from this count.
GRFEXPORT uint32_t grf_filecount(grf_handle handle);

/// Returns the amount of data (in bytes) that would be theorically saved if the file gets repacked.
GRFEXPORT uint32_t grf_wasted_space(grf_handle handle);

/// *****************************************************************************
/// **************************** FILES FUNCTIONS ********************************
/// *****************************************************************************

/// Add a file to the specified GRF file (opened for writing) and return a handle to the created file.
GRFEXPORT grf_node grf_file_add(grf_handle handle, const char* name, void* buffer, size_t size);
GRFEXPORT grf_node grf_file_add_fd(grf_handle handle, const char* name, int fd);
GRFEXPORT grf_node grf_file_add_path(grf_handle handle, const char* name, const char* file);

/// Returns a node handle to the specified file inside the GRF file.
/// If the function fails, NULL is returned.
GRFEXPORT grf_node grf_get_file(grf_handle handle, const char* filename);

/// Returns the full filename of a file.
GRFEXPORT const char* grf_file_get_filename(grf_node node);

/// Returns only the filename (no path info)
GRFEXPORT const char* grf_file_get_basename(grf_node node);

/// Returns the (real) size of the file.
GRFEXPORT uint32_t grf_file_get_size(grf_node node);

/// Returns the position of the file in the archive.
GRFEXPORT uint32_t grf_file_get_storage_pos(grf_node node);

/// Returns the real (compressed) size of the file.
GRFEXPORT uint32_t grf_file_get_storage_size(grf_node node);

/// Returns the archive flags of the file.
/// Test if the first bit is set to know if a node is a file, or not.
GRFEXPORT uint32_t grf_file_get_storage_flags(grf_node node);

/// Extracts the file to the provided buffer and returns the number of bytes successfully extracted.
/// Assumes that at least grf_file_get_size() bytes can be safely written into ptr.
GRFEXPORT uint32_t grf_file_get_contents(grf_node node, void* ptr);

/// Extracts a file to the specified file descriptor.
/// This can be a socket or a regular file, no seeks are used.
GRFEXPORT uint32_t grf_file_put_contents_to_fd(grf_node node, int fd);

/// Write the contents of the compressed file to the filesystem.
GRFEXPORT bool grf_put_contents_to_file(grf_node node, const char* filename);

/// Rename the given file to new_name.
/// You must provide the full path, eg: "data\file.txt".
/// Renaming to an already existing file will delete it.
/// Renaming to a directory name will give unexpected results.
GRFEXPORT bool grf_file_rename(grf_node node, const char* new_name);

/// Removes a file from the GRF (marking its data as free space).
GRFEXPORT bool grf_file_delete(grf_node node);

/// *****************************************************************************
/// ******************************* TREE FUNCTIONS ******************************
/// *****************************************************************************
/// Trees are a good way to represent the content of a GRF file in a hierarchical way.
/// Use grf_create_tree() to build a global directories/files-based tree for the GRF,
/// allowing useful things such as listing the contents of a directory directly.

/// Create a tree for the specified GRF file.
GRFEXPORT void grf_create_tree(grf_handle handle);

/// Returns the root node of the tree.
GRFEXPORT grf_treenode grf_tree_get_root(grf_handle handle);

/// Returns a null-terminated array of files/directories found in a specific node.
/// Note: The result has to be free()'d by the caller.
GRFEXPORT grf_treenode* grf_tree_list_node(grf_treenode node);

/// Return true if this node is a tree, false if it's a regular file.
GRFEXPORT bool grf_tree_is_dir(grf_treenode node);

/// Returns the name of a node (no path info).
GRFEXPORT const char* grf_tree_get_name(grf_treenode node);

/// Return the node of the file attached to the current treenode.
/// Will return NULL for directories (but do not rely on that, use grf_tree_is_dir()).
GRFEXPORT grf_node grf_tree_get_file(grf_treenode node);

/// Returns the parent node, or NULL if you call that on the root node.
GRFEXPORT grf_treenode grf_tree_get_parent(grf_treenode node);

/// Returns the grf_treenode entry corresponding to a file.
/// Will return NULL if the treenode wasn't created previously.
GRFEXPORT grf_treenode grf_file_get_tree(grf_node node);

/// Returns the number of files contained in a treenode dir.
GRFEXPORT uint32_t grf_tree_dir_count_files(grf_treenode node);

/// *****************************************************************************
/// ************************** FILE LISTING FUNCTIONS ***************************
/// *****************************************************************************

/// Returns a null-terminated array of nodes contained in the GRF file.
/// The order is totally random, so don't rely on it.
/// The result has to be free()'d by he caller.
///
/// DEPRECATED: Please use grf_get_file_id_list() instead.
/// Same output, but you don't have to free the list afterwards, and it's not cpu-intensive.
GRFEXPORT grf_node* grf_get_file_list(grf_handle handle);

/// Returns the first file's node pointer in the GRF, or NULL if no file were found.
GRFEXPORT grf_node grf_get_file_first(grf_handle handle);

/// Returns next file, or NULL if file was last file.
GRFEXPORT grf_node grf_get_file_next(grf_node node);

/// Returns previous file, or NULL if file is already the first one.
GRFEXPORT grf_node grf_get_file_prev(grf_node node);

/// *****************************************************************************
/// ***************************** FILE ID FUNCTIONS *****************************
/// *****************************************************************************

/// Returns the global list of files. The order may change each time you call grf_update_id_list().
GRFEXPORT grf_node* grf_get_file_id_list(grf_handle handle);

/// Recompute the ID list (needed after add/remove).
GRFEXPORT void grf_update_id_list(grf_handle handle);

/// Returns ID (index position in the list) of a file.
GRFEXPORT uint32_t grf_file_get_id(grf_node node);

/// Returns the file corresponding to the given ID.
GRFEXPORT grf_node grf_get_file_by_id(grf_handle handle, uint32_t id);

/// *****************************************************************************
/// *************************** GRF MASS OPERATIONS *****************************
/// *****************************************************************************

/// Repack given GRF file, to save wasted_bytes and maybe more (if using the recompress option).
/// Valid options :
/// - GRF_REPACK_FAST (just move files)
/// - GRF_REPACK_DECRYPT (move files, and decrypt files if any was found encrypted)
/// - GRF_REPACK_RECOMPRESS (recompress all files, and replace if newly compressed file is smaller than the one previously stored)
GRFEXPORT bool grf_repack(grf_handle handle, uint8_t options);

/// Copy files from "src" grf_handle (can be opened read-only) to "dest" grf_handle (must be opened read/write).
/// Takes the same options as grf_repack with one exception :
///   GRF_REPACK_RECOMPRESS will recompress all files, even if the new one is larger than the not-recompressed one.
GRFEXPORT bool grf_merge(grf_handle dest, grf_handle src, uint8_t options);

#ifdef __cplusplus
}
#endif
